package com.simple.mipc.entity;

import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.Parcelable;
import android.text.TextUtils;

import com.simple.mipc.annotation.Param;
import com.simple.mipc.annotation.Res;
import com.simple.mipc.handler.MessageHandler;
import com.simple.mipc.utils.TypeUtil;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import lombok.SneakyThrows;

public class Worker {

    private String res;

    private Method method;

    private Object instance;

    private List<Param> params;

    public Worker(Method method) {
        this.method = method;
        Res res = method.getDeclaredAnnotation(Res.class);
        if (Objects.isNull(res)) {
            throw new IllegalArgumentException(String.format("缺少注解：%s，无法解析访问的远程接口", Res.class));
        }
        assert res != null;
        this.res = res.value();
        this.res = TextUtils.isEmpty(this.res) ? method.getName() : this.res;
        params = Arrays.stream(method.getParameters()).filter(parameter -> {
                    Param param = parameter.getDeclaredAnnotation(Param.class);
                    if (Objects.isNull(param)) {
                        throw new IllegalArgumentException(String.format("缺少注解：%s，不能找到申请时的参数名", Param.class));
                    }
                    return true;
                }).map(parameter -> parameter.getDeclaredAnnotation(Param.class))
                .collect(Collectors.toList());
    }

    @SneakyThrows
    public void work(Messenger messenger, Messenger handler, Object... args) {
        int size = params.size();
        int len = Objects.isNull(args) ? 0 : args.length;
        if (len != size) {
            throw new IllegalArgumentException(String.format("参数异常，参数数量与注解：%s，定义的数量不一致", Param.class));
        }
        Message obtain = Message.obtain();
        Bundle bundle = obtain.getData();
        bundle.putInt("what", MessageHandler.APPLY_FUNCTION_CODE);
        obtain.replyTo = handler;
        bundle.putString(MessageHandler.FUNCTION_RES, res);
        for (int i = 0; i < params.size(); i++) {
            Param param = params.get(i);
            Object arg = args[i];
            putBundle(bundle, param, arg);
        }
        messenger.send(obtain);
    }

    private void putBundle(Bundle bundle, Param param, Object arg) {
        Class<?> aClass = arg.getClass();
        String key = param.value();
        if (Objects.equals(aClass, byte.class) || Objects.equals(aClass, Byte.class)) {
            bundle.putByte(key, (Byte) arg);
        } else if (Objects.equals(aClass, short.class) || Objects.equals(aClass, Short.class)) {
            bundle.putShort(key, (Short) arg);
        } else if (Objects.equals(aClass, char.class) || Objects.equals(aClass, Character.class)) {
            bundle.putChar(key, (Character) arg);
        } else if (Objects.equals(aClass, int.class) || Objects.equals(aClass, Integer.class)) {
            bundle.putInt(key, (Integer) arg);
        } else if (Objects.equals(aClass, float.class) || Objects.equals(aClass, Float.class)) {
            bundle.putFloat(key, (Float) arg);
        } else if (Objects.equals(aClass, long.class) || Objects.equals(aClass, Long.class)) {
            bundle.putLong(key, (Long) arg);
        } else if (Objects.equals(aClass, double.class) || Objects.equals(aClass, Double.class)) {
            bundle.putDouble(key, (Double) arg);
        } else if (Objects.equals(aClass, boolean.class) || Objects.equals(aClass, Boolean.class)) {
            bundle.putBoolean(key, (Boolean) arg);
        } else if (arg instanceof String) {
            bundle.putString(key, String.valueOf(arg));
        } else if (arg instanceof Parcelable) {
            bundle.putParcelable(key, (Parcelable) arg);
        } else if (arg instanceof Serializable) {
            bundle.putSerializable(key, (Serializable) arg);
        } else if (arg instanceof IBinder) {
            bundle.putBinder(key, (IBinder) arg);
        } else if (arg instanceof Bundle) {
            bundle.putBundle(key, (Bundle) arg);
        } else if (arg instanceof byte[]) {
            bundle.putByteArray(key, (byte[]) arg);
        } else if (arg instanceof short[]) {
            bundle.putShortArray(key, (short[]) arg);
        } else if (arg instanceof char[]) {
            bundle.putCharArray(key, (char[]) arg);
        } else if (arg instanceof CharSequence) {
            bundle.putCharSequence(key, (CharSequence) arg);
        } else if (arg instanceof int[]) {
            bundle.putIntArray(key, (int[]) arg);
        } else if (arg instanceof float[]) {
            bundle.putFloatArray(key, (float[]) arg);
        } else if (arg instanceof long[]) {
            bundle.putLongArray(key, (long[]) arg);
        } else if (arg instanceof double[]) {
            bundle.putDoubleArray(key, (double[]) arg);
        } else if (arg instanceof Parcelable[]) {
            bundle.putParcelableArray(key, (Parcelable[]) arg);
        } else if (arg instanceof CharSequence[]) {
            bundle.putCharSequenceArray(key, (CharSequence[]) arg);
        } else if (arg instanceof ArrayList) {
            Class<?> type = TypeUtil.getType(arg);
            if (CharSequence.class.isAssignableFrom(type)) {
                bundle.putCharSequenceArrayList(key, (ArrayList<CharSequence>) arg);
            } else if (Objects.equals(type, Integer.class)) {
                bundle.putIntegerArrayList(key, (ArrayList<Integer>) arg);
            } else if (Objects.equals(type, String.class)) {
                bundle.putStringArrayList(key, (ArrayList<String>) arg);
            } else if (Parcelable.class.isAssignableFrom(type)) {
                bundle.putParcelableArrayList(key, (ArrayList<? extends Parcelable>) arg);
            } else {
                throw new IllegalArgumentException(String.format("不支持的泛型类型：%s", type));
            }
        } else {
            throw new IllegalArgumentException(String.format("不支持的类型：%s", aClass));
        }
    }
}
